//
//  CoreTextLabel.m
//  Weshare
//
//  Created by KevinCao on 16/3/24.
//  Copyright © 2016年 wxwl. All rights reserved.
//

#import "CoreTextLabel.h"
#import <CoreText/CoreText.h>

@implementation CoreTextLabel

- (void)setAttributedText:(NSAttributedString *)attributedText
{
    _attributedText = attributedText;
    
    NSDictionary *tokenAttributes = [self.attributedText attributesAtIndex:0 effectiveRange:NULL];
    if (!self.font && [tokenAttributes objectForKey:NSFontAttributeName]) {
        self.font = [tokenAttributes objectForKey:NSFontAttributeName];
    }
    if ([tokenAttributes objectForKey:NSParagraphStyleAttributeName]) {
        NSParagraphStyle *paragraph = [tokenAttributes objectForKey:NSParagraphStyleAttributeName];
        if (paragraph.lineSpacing) {
            self.lineSpacing = paragraph.lineSpacing;
        }
    }
    CGSize textSize = [HDUtil sizeWithString:attributedText size:CGSizeMake(self.width, CGFLOAT_MAX)];
    CGFloat lineHeight = self.font.pointSize + self.lineSpacing;
    CGFloat height = (self.numberOfLines?:1) * lineHeight;
    CGFloat textHeight = textSize.height+self.lineSpacing;
    self.height = height>textHeight?textHeight:height;
}

- (void)drawRect:(CGRect)rect
{
    if (!self.attributedText.length) {
        return;
    }
    
    CGFloat textHeight = [HDUtil sizeWithString:self.attributedText size:CGSizeMake(self.width, CGFLOAT_MAX)].height;
    textHeight = textHeight + self.lineSpacing;
    
    // 创建绘制区域，path的高度对绘制有直接影响，如果高度不够，则计算出来的CTLine的数量会少一行或者少多行
    CGMutablePathRef path = CGPathCreateMutable();
    CGPathAddRect(path, NULL, CGRectMake(0, 0, CGRectGetWidth(self.bounds), textHeight*2));
    
    // 根据NSAttributedString生成CTFramesetterRef
    CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)self.attributedText);
    
    CTFrameRef ctFrame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, self.attributedText.length), path, NULL);
    
    // 重置高度
    CGFloat realHeight = textHeight;
    // 绘制全部文本需要的高度大于实际高度则调整，并加上省略号
    if (realHeight > CGRectGetHeight(self.frame))
    {
        realHeight = CGRectGetHeight(self.frame);
    }
    
    // 获取上下文
    CGContextRef contextRef = UIGraphicsGetCurrentContext();
    
    // 转换坐标系
    CGContextSetTextMatrix(contextRef, CGAffineTransformIdentity);
    CGContextTranslateCTM(contextRef, 0, realHeight); // 这里跟着调整
    CGContextScaleCTM(contextRef, 1.0, -1.0);
    
    // 这里可调整可不调整
    CGPathAddRect(path, NULL, CGRectMake(0, 0, CGRectGetWidth(self.bounds), realHeight));
    
    // 一行一行绘制
    CFArrayRef lines = CTFrameGetLines(ctFrame);
    CFIndex lineCount = CFArrayGetCount(lines);
    CGPoint lineOrigins[CFArrayGetCount(lines)];
    
    // 把ctFrame里每一行的初始坐标写到数组里，注意CoreText的坐标是左下角为原点
    CTFrameGetLineOrigins(ctFrame, CFRangeMake(0, 0), lineOrigins);
    
    CGFloat frameY = 0;
    
    CGFloat lineHeight = self.font.pointSize + self.lineSpacing;
    
    for (CFIndex i = 0; i < lineCount; i++)
    {
        // 遍历每一行CTLine
        CTLineRef line = CFArrayGetValueAtIndex(lines, i);
        
        
        CGFloat lineAscent;
        CGFloat lineDescent;
        CGFloat lineLeading; // 行距
        // 该函数除了会设置好ascent,descent,leading之外，还会返回这行的宽度
        CTLineGetTypographicBounds(line, &lineAscent, &lineDescent, &lineLeading);
        
        CGPoint lineOrigin = lineOrigins[i];
        
        // 微调Y值，需要注意的是CoreText的origin的Y值是在baseLine处，而不是下方的descent。
        
        
        // 调节self.font.descender该值可改变文字排版的上下间距，此处下间距为0
        frameY = realHeight - (i + 1)*lineHeight - self.font.descender;
        
        lineOrigin.y = frameY;
        
        // 调整坐标
        CGContextSetTextPosition(contextRef, lineOrigin.x, lineOrigin.y);
        
        // 反转坐标系
        frameY = realHeight - frameY;
        
        // 行高
        CGFloat heightPerLine = self.font.pointSize + self.lineSpacing;
        
        if (realHeight - frameY > heightPerLine)
        {
            CTLineDraw(line, contextRef);
            
        } else
        {
            // 最后一行，加上省略号
            static NSString* const kEllipsesCharacter = @"\u2026";
            
            CFRange lastLineRange = CTLineGetStringRange(line);
            
            if (lastLineRange.location + lastLineRange.length < (CFIndex)self.attributedText.length)
            {
                // 这一行放不下所有的字符（下一行还有字符），则把此行后面的回车、空格符去掉后，再把最后一个字符替换成省略号
                
                CTLineTruncationType truncationType = kCTLineTruncationEnd;
                NSUInteger truncationAttributePosition = lastLineRange.location + lastLineRange.length - 1;
                
                // 拿到最后一个字符的属性字典
                NSDictionary *tokenAttributes = [self.attributedText attributesAtIndex:truncationAttributePosition
                                                               effectiveRange:NULL];
                // 给省略号字符设置字体大小、颜色等属性
                NSAttributedString *tokenString = [[NSAttributedString alloc] initWithString:kEllipsesCharacter
                                                                                  attributes:tokenAttributes];
                
                // 用省略号单独创建一个CTLine，下面在截断重新生成CTLine的时候会用到
                CTLineRef truncationToken = CTLineCreateWithAttributedString((__bridge CFAttributedStringRef)tokenString);
                
                // 把这一行的属性字符串复制一份，如果要把省略号放到中间或其他位置，只需指定复制的长度即可
                NSUInteger copyLength = lastLineRange.length-1;
                
                NSMutableAttributedString *truncationString = [[self.attributedText attributedSubstringFromRange:NSMakeRange(lastLineRange.location, copyLength)] mutableCopy];
                
                if (lastLineRange.length > 1)
                {
                    // Remove any whitespace at the end of the line.
                    unichar lastCharacter = [[truncationString string] characterAtIndex:copyLength - 1];
                    
                    // 如果复制字符串的最后一个字符是换行、空格符，则删掉
                    if ([[NSCharacterSet whitespaceAndNewlineCharacterSet] characterIsMember:lastCharacter])
                    {
                        [truncationString deleteCharactersInRange:NSMakeRange(copyLength - 1, 1)];
                    }
                }
                
                // 拼接省略号到复制字符串的最后
                [truncationString appendAttributedString:tokenString];
                
                // 把新的字符串创建成CTLine
                CTLineRef truncationLine = CTLineCreateWithAttributedString((__bridge CFAttributedStringRef)truncationString);
                
                // 创建一个截断的CTLine，该方法不能少，具体作用还有待研究
                CTLineRef truncatedLine = CTLineCreateTruncatedLine(truncationLine, self.frame.size.width, truncationType, truncationToken);
                
                if (!truncatedLine)
                {
                    // If the line is not as wide as the truncationToken, truncatedLine is NULL
                    truncatedLine = CFRetain(truncationToken);
                }
                
                CFRelease(truncationLine);
                CFRelease(truncationToken);
                
                CTLineDraw(truncatedLine, contextRef);
                CFRelease(truncatedLine);
                
            } else
            {
                
                // 这一行刚好是最后一行，且最后一行的字符可以完全绘制出来
                CTLineDraw(line, contextRef);
            }
            
            // 跳出循环，避免绘制剩下的多余的CTLine
            break;
            
        }
        
    }
    
    CFRelease(path);
    CFRelease(framesetter);
    CFRelease(ctFrame);
    
}
@end
